#include "../include/db_connection_pool.h"

db_connection_pool *db_connection_pool::connPool = nullptr;

db_connection_pool *db_connection_pool::GetInstance(std::string url, std::string user, std::string passwd, std::string db_name, int port, uint8_t max_conn)
{
    if (connPool == nullptr)
        connPool = new db_connection_pool(url, user, passwd, db_name, port, max_conn);
    return connPool;
}

db_connection_pool::db_connection_pool(std::string url, std::string user, std::string passwd, std::string db_name, int port, uint8_t max_conn)
{
    this->URL = url;
    this->PORT = port;
    this->USER = user;
    this->PASSWORD = passwd;
    this->DBNAME = db_name;

    try
    {
        this->driver = sql::mysql::get_driver_instance();
    }
    catch (sql::SQLException &e)
    {
        perror("驱动连接出错：\n");
    }
    catch (std::runtime_error &e)
    {
        perror("运行时错误：\n");
    }

    sql::Connection *conn = nullptr;
    lock.lock();
    for (int i = 0; i < max_conn; ++i)
    {
        conn = CreateConnection();
        if (conn)
        {
            connList.emplace_back(conn);
            ++(this->FreeConn);
        }
        else
            perror("创建Connection出错：");
    }
    lock.unlock();

    reserve = sem(FreeConn);
    MaxConn = FreeConn;
}

sql::Connection *db_connection_pool::CreateConnection()
{
    sql::Connection *conn;
    try
    {
        conn = driver->connect(this->URL, this->USER, this->PASSWORD);
        conn->setSchema(this->DBNAME);
        return conn;
    }
    catch (sql::SQLException &e)
    {
        perror("创建连接出错：");
        return nullptr;
    }
    catch (std::runtime_error &e)
    {
        perror("运行时出错：");
        return nullptr;
    }
}

sql::Connection *db_connection_pool::GetConnection()
{
    sql::Connection *conn;
    // 连接池中还有连接
    if (connList.size() == 0)
        return nullptr;

    // 信号量-1
    reserve.wait();
    lock.lock();
    conn = connList.front();
    connList.pop_front();
    // 若连接已被关闭，则新建一个
    if (conn->isClosed())
    {
        delete conn;
        conn = this->CreateConnection();
    }
    --FreeConn;
    ++CurConn;
    lock.unlock();

    return conn;
}

bool db_connection_pool::ReleaseConnect(sql::Connection *conn)
{
    if (conn == nullptr)
        return false;

    lock.lock();
    connList.emplace_back(conn);
    ++FreeConn;
    --CurConn;
    lock.unlock();

    reserve.post();
    return true;
}

void db_connection_pool::DestroyPool()
{
    lock.lock();
    if (connList.size() > 0)
    {
        for (auto it = connList.begin(); it != connList.end(); ++it)
        {
            sql::Connection *conn = *it;
            if (conn)
            {
                try
                {
                    conn->close();
                }
                catch (sql::SQLException &e)
                {
                    perror(e.what());
                }
                catch (std::runtime_error &e)
                {
                    perror(e.what());
                }
                delete conn;
            }
        }
        CurConn = 0;
        FreeConn = 0;
        connList.clear();
    }
    lock.unlock();
}

connRAII::connRAII(sql::Connection **conn, db_connection_pool *connPool)
{
    *conn = connPool->GetConnection();
    conRAII = *conn;
    poolRAII = connPool;
}

connRAII::~connRAII()
{
    poolRAII->ReleaseConnect(conRAII);
}
